package com.simen.kv;

import android.annotation.TargetApi;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * 核心处理类
 * Created by zhangming on 2017/4/12.
 */
public class KVManager {
    private static final String TAG = "KVManager";
    private final static String DBNAME = "simple_kv";
    private final static int VERSION = 1;
    private static KVManager instance;
    private Context mApplicationContext;
    private SQLiteOpenHelper liteOpenHelper;
    
    public static KVManager getInstance() {
        if (instance == null) {
            synchronized (KVManager.class) {
                if (instance == null) {
                    instance = new KVManager();
                }
            }
        }
        return instance;
    }
    
    private KVManager() {
    
    }
    
    public void init(Context context) {
        this.mApplicationContext = context.getApplicationContext();
        this.liteOpenHelper = new SQLiteHelper(mApplicationContext, DBNAME, VERSION);
        
        //应用第一次打开时,使用读写属性获取数据库实例,防止出现版本异常
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        if (writableDatabase != null && writableDatabase.isOpen()) {
            writableDatabase.close();
        }
    }
    
    SQLiteOpenHelper getLiteOpenHelper() {
        return liteOpenHelper;
    }
    
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public void setWriteAheadLoggingEnabled(boolean enabled) {
        liteOpenHelper.setWriteAheadLoggingEnabled(enabled);
    }
    
    /**
     * 添加表
     *
     * @param table     表名称
     * @param classType 主键类型（目前支持String、Integer、Long主键）
     */
    public void addTable(String table, Class classType) {
        if (TextUtils.isEmpty(table)) {
            Log.e(TAG, "addTable: table name can't be null or \"\"");
            return;
        }
        
        if (classType == Integer.class || classType == Long.class) {
            createIntegerTable(table);
        } else if (classType == String.class) {
            createTextTable(table);
        } else if (classType == Byte.class) {
            createBlobTable(table);
        } else {
            Log.d(TAG, "addTable: unsupported classType");
        }
    }
    
    /**
     * 创建字符串型主键表
     *
     * @param table 表名称
     */
    private void createTextTable(String table) {
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        //开启事务
        writableDatabase.beginTransaction();
        try {
            //批量处理操作
            writableDatabase.execSQL(Constants.CREATETEXTKEY.replace(Constants.DBNAME, table));
            //设置事务标志为成功，当结束事务时就会提交事务
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
        } finally {
            //结束事务
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
    }
    
    /**
     * 创建整型主键表
     *
     * @param table 表名称
     */
    private void createIntegerTable(String table) {
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        writableDatabase.beginTransaction();
        try {
            writableDatabase.execSQL(Constants.CREATEINTEGERKEY.replace(Constants.DBNAME, table));
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
        } finally {
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
    }
    
    /**
     * 创建二进制型主键表
     *
     * @param table 表名称
     */
    private void createBlobTable(String table) {
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        writableDatabase.beginTransaction();
        try {
            writableDatabase.execSQL(Constants.CREATEBLOBKEY.replace(Constants.DBNAME, table));
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
        } finally {
            //结束事务
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
    }
    
    /**
     * 本接口提供安全插入功能，如果记录key已存在，则刷新其值；不存在，则插入新记录
     *
     * @param <T>   这是泛型参数
     * @param table 表名称
     * @param key   key
     * @param value 插入值
     * @return true, 插入成功；false,插入异常
     */
    public <T> boolean update(String table, String key, T value) {
        return update(liteOpenHelper.getWritableDatabase(),table,Columns.KEYNAME,key,Columns.VALUENAME,value);
    }

    /**
     * 本接口提供安全插入功能，如果记录key已存在，则刷新其值；不存在，则插入新记录
     *
     * @param <T>   这是泛型参数
     * @param writableDatabase 数据库
     * @param table 表名称
     * @param key   key
     * @param value 插入值
     * @return true, 插入成功；false,插入异常
     */
    private static <T> boolean update(SQLiteDatabase writableDatabase,String table,String keyColumnName,String key,String valueColumnName, T value) {
        //开启事务
        writableDatabase.beginTransaction();
        try {
            //批量处理操作
            Cursor cursor = writableDatabase.rawQuery("select * from " + table + " where " + keyColumnName + "=?", new String[]{key});
            ContentValues contentValues = new ContentValues();
            if (value instanceof String) {
                contentValues.put(valueColumnName, (String) value);
            } else if (value instanceof Integer) {
                contentValues.put(valueColumnName, (Integer) value);
            } else if (value instanceof byte[]) {
                contentValues.put(valueColumnName, (byte[]) value);
            } else {
                Log.e(TAG, "unsupported data type! ");
                return false;
            }
            long TimeMillis = System.currentTimeMillis();
            contentValues.put(Columns.UPDATETIMENAME, TimeMillis);
            
            if (cursor != null) {
                if (cursor.moveToNext()) {
                    //该记录已存在，更新即可
                    int _id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
                    
                    int affects = writableDatabase.updateWithOnConflict(table, contentValues,
                            BaseColumns._ID +
                                    "=?", new String[]{Integer.toString(_id)}, SQLiteDatabase
                                    .CONFLICT_NONE);
                } else {
                    contentValues.put(keyColumnName, key);
                    contentValues.put(Columns.CREATETIMENAME, TimeMillis);
                    //插入新记录
                    long rowId = writableDatabase.insert(table, null, contentValues);
                }
                cursor.close();
            } else {
                contentValues.put(keyColumnName, key);
                contentValues.put(Columns.CREATETIMENAME, TimeMillis);
                //插入新记录
                long rowId = writableDatabase.insert(table, null, contentValues);
            }
            //设置事务标志为成功，当结束事务时就会提交事务
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //结束事务
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
        return true;
    }
    
    /**
     * 筛选查询大量数据
     *
     * @param table 表名称
     * @param where 查询条件
     * @return 返回符合条件的数据集
     */
    public List getKVs(String table, Where where) {
        SQLiteDatabase readableDatabase = liteOpenHelper.getReadableDatabase();
        readableDatabase.beginTransaction();
        List<KV> list = new ArrayList<>();
        try {
            Cursor cursor = readableDatabase.rawQuery("select * from " + table + KVUtils.whereToSQL
                    (where), null);
            if (cursor != null) {
                if (cursor.moveToNext()) {
                    int type = cursor.getType(cursor.getColumnIndex(Columns.VALUENAME));
                    switch (type) {
                        case Cursor.FIELD_TYPE_BLOB:
                            list.add(new KVBlobImpl(cursor));
                            break;
                        case Cursor.FIELD_TYPE_INTEGER:
                            list.add(new KVLongImpl(cursor));
                            break;
                        case Cursor.FIELD_TYPE_STRING:
                            list.add(new KVStringImpl(cursor));
                            break;
                        default:
                            Log.e(TAG, "unsupported data type! " + type);
                            break;
                    }
                    
                    while (cursor.moveToNext()) {
                        switch (type) {
                            case Cursor.FIELD_TYPE_BLOB:
                                list.add(new KVBlobImpl(cursor));
                                break;
                            case Cursor.FIELD_TYPE_INTEGER:
                                list.add(new KVLongImpl(cursor));
                                break;
                            case Cursor.FIELD_TYPE_STRING:
                                list.add(new KVStringImpl(cursor));
                                break;
                            default:
                                Log.e(TAG, "unsupported data type! " + type);
                                break;
                        }
                    }
                }
                cursor.close();
            }
            readableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readableDatabase.endTransaction();
            readableDatabase.close();
        }
        return list;
    }
    
    /**
     * 查询指定表中的KV记录
     *
     * @param table 表名称
     * @param key   key名称
     * @return 没有找到对应的记录时，返回null
     */
    public KV getKV(String table, String key) {
        if (TextUtils.isEmpty(key)) return null;
        
        SQLiteDatabase readableDatabase = liteOpenHelper.getReadableDatabase();
        readableDatabase.beginTransaction();
        try {
            Cursor cursor = readableDatabase.rawQuery("select * from " + table + " where " + Columns
                    .KEYNAME + "=?", new String[]{key});
            if (cursor != null) {
                if (cursor.moveToNext()) {
                    int type = cursor.getType(cursor.getColumnIndex(Columns.VALUENAME));
                    KV kv = null;
                    switch (type) {
                        case Cursor.FIELD_TYPE_BLOB:
                            kv = new KVBlobImpl(cursor);
                            break;
                        case Cursor.FIELD_TYPE_INTEGER:
                            kv = new KVLongImpl(cursor);
                            break;
                        case Cursor.FIELD_TYPE_STRING:
                            kv = new KVStringImpl(cursor);
                            break;
                        default:
                            Log.e(TAG, "unsupported data type! " + type);
                            break;
                    }
                    cursor.close();
                    readableDatabase.setTransactionSuccessful();
                    return kv;
                }
                cursor.close();
            }
            readableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readableDatabase.endTransaction();
            readableDatabase.close();
        }
        return null;
    }
    
    /**
     * 查询指定表的指定主键的Value
     *
     * @param table 表名称
     * @param key   主键名称
     * @return 返回查询到值，查询失败或者没有记录时返回null
     */
    public String get(String table, String key) {
        if (TextUtils.isEmpty(key)) return null;
        
        SQLiteDatabase readableDatabase = liteOpenHelper.getReadableDatabase();
        readableDatabase.beginTransaction();
        try {
            Cursor cursor = readableDatabase.rawQuery("select * from " + table + " where " + Columns
                    .KEYNAME + "=?", new String[]{key});
            if (cursor != null) {
                if (cursor.moveToNext()) {
                    String value = cursor.getString(cursor.getColumnIndex(Columns.VALUENAME));
                    cursor.close();
                    readableDatabase.setTransactionSuccessful();
                    return value;
                }
                cursor.close();
            }
            readableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readableDatabase.endTransaction();
            readableDatabase.close();
        }
        return null;
    }
    
    /**
     * 查询指定表中是否包含指定key
     *
     * @param table 表名称
     * @param key   key名称
     * @return true，该表包含指定key
     */
    public boolean contains(String table, String key) {
        if (TextUtils.isEmpty(key)) return false;
        
        SQLiteDatabase readableDatabase = liteOpenHelper.getReadableDatabase();
        readableDatabase.beginTransaction();
        try {
            Cursor cursor = readableDatabase.rawQuery("select * from " + table + " where " + Columns
                    .KEYNAME + "=?", new String[]{key});
            if (cursor != null) {
                if (cursor.moveToNext()) {
                    cursor.close();
                    return true;
                }
                cursor.close();
            }
            readableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readableDatabase.endTransaction();
            readableDatabase.close();
        }
        return false;
    }
    
    /**
     * 判断数据表是否为空
     *
     * @param table 表名称
     * @return true, 表中没有数据;false,表中有数据
     */
    public boolean isTableEmpty(String table) {
        SQLiteDatabase readableDatabase = liteOpenHelper.getReadableDatabase();
        readableDatabase.beginTransaction();
        try {
            Cursor cursor = readableDatabase.rawQuery("select * from " + table, null);
            if (cursor != null) {
                
                if (cursor.getCount() > 0) {
                    cursor.close();
                    return true;
                }
                
                cursor.close();
            }
            
            readableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readableDatabase.endTransaction();
            readableDatabase.close();
        }
        return false;
    }
    
    /**
     * 移除指定行
     *
     * @param table 表名称
     * @param key   主键名称
     * @return true，处理成功；false，处理失败
     */
    public boolean remove(String table, String key) {
        return remove(table, new Where().equals(key, Columns.KEYNAME));
    }
    
    /**
     * 移除指定行
     *
     * @param table 表名称
     * @param where 约束条件
     * @return true，处理成功；false，处理失败
     */
    public boolean remove(String table, Where where) {
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        writableDatabase.beginTransaction();
        try {
            writableDatabase.execSQL("delete from " + table + KVUtils.whereToSQL(where));
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
        return true;
    }
    
    /**
     * 清空表数据
     *
     * @param table 表名称
     * @return true，清空成功；false，清空失败
     */
    public boolean clear(String table) {
        SQLiteDatabase writableDatabase = liteOpenHelper.getWritableDatabase();
        writableDatabase.beginTransaction();
        try {
            writableDatabase.execSQL("delete from " + table);
            writableDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            writableDatabase.endTransaction();
            writableDatabase.close();
        }
        return true;
    }
}
